pub(crate) fn print() {
    println!("\n自定义类型 ============");

    structure();
    enumeration();
    test_enumeration_list();
    const_and_static();
}

/*
  自定义类型
    Rust 自定义数据类型主要是通过下面这两个关键字来创建：

    struct： 定义一个结构体（structure）
    enum： 定义一个枚举类型（enumeration）
    而常量（constant）可以通过 const 和 static 关键字来创建。
 */


/*
  结构体
    结构体（structure，缩写成 struct）有 3 种类型，使用 struct 关键字来创建：

    元组结构体（tuple struct），事实上就是具名元组而已。
    经典的 C 语言风格结构体（C struct）。
    单元结构体（unit struct），不带字段，在泛型中很有用。
 */
fn structure() {
    #[derive(Debug)]
    struct Person {
        name: String,
        age: u8,
    }

    // 单元结构体
    struct Unit;

    // 元组结构体
    struct Pair(i32, f32);

    // 带有两个字段的结构体
    struct Point {
        x: f32,
        y: f32,
    }

    // 结构体可以作为另一个结构体的字段
    #[allow(dead_code)]
    struct Rectangle {
        // 可以在空间中给定左上角和右下角在空间中的位置来指定矩形。
        top_left: Point,
        bottom_right: Point,
    }

    // 使用简单的写法初始化字段，并创建结构体
    let name = String::from("Peter");
    let age = 27;
    let peter = Person { name, age };

    // 以 Debug 方式打印结构体
    println!("{:?}", peter);

    // 实例化结构体 `Point`
    let point: Point = Point { x: 10.3, y: 0.4 };

    // 访问 point 的字段
    println!("point coordinates: ({}, {})", point.x, point.y);

    // 使用结构体更新语法创建新的 point，
    // 这样可以用到之前的 point 的字段
    let bottom_right = Point { x: 5.2, ..point };

    // `bottom_right.y` 与 `point.y` 一样，因为这个字段就是从 `point` 中来的
    println!("second point: ({}, {})", bottom_right.x, bottom_right.y);

    // 使用 `let` 绑定来解构 point
    let Point { x: left_edge, y: top_edge } = point;

    let _rectangle = Rectangle {
        // 结构体的实例化也是一个表达式
        top_left: Point { x: left_edge, y: top_edge },
        bottom_right,
    };

    // 实例化一个单元结构体
    let _unit = Unit;

    // 实例化一个元组结构体
    let pair = Pair(1, 0.1);

    // 访问元组结构体的字段
    println!("pair contains {:?} and {:?}", pair.0, pair.1);

    // 解构一个元组结构体
    let Pair(integer, decimal) = pair;

    println!("pair contains {:?} and {:?}", integer, decimal);
}

/*
 枚举
   enum 关键字允许创建一个从数个不同取值中选其一的枚举类型（enumeration）。任何一个在 struct 中合法的取值在 enum 中也合法。
 */
fn enumeration() {
    // 创建一个 `enum`（枚举）来对 web 事件分类。注意变量名和类型共同指定了 `enum`
    // 取值的种类：`PageLoad` 不等于 `PageUnload`，`KeyPress(char)` 不等于
    // `Paste(String)`。各个取值不同，互相独立。
    enum WebEvent {
        // 一个 `enum` 可以是单元结构体（称为 `unit-like` 或 `unit`），
        PageLoad,
        PageUnload,
        // 或者一个元组结构体，
        KeyPress(char),
        Paste(String),
        // 或者一个普通的结构体。
        Click { x: i64, y: i64 },
    }

    // 此函数将一个 `WebEvent` enum 作为参数，无返回值。
    fn inspect(event: WebEvent) {
        match event {
            WebEvent::PageLoad => println!("page loaded"),
            WebEvent::PageUnload => println!("page unloaded"),
            // 从 `enum` 里解构出 `c`。
            WebEvent::KeyPress(c) => println!("pressed '{}'.", c),
            WebEvent::Paste(s) => println!("pasted \"{}\".", s),
            // 把 `Click` 解构给 `x` and `y`。
            WebEvent::Click { x, y } => {
                println!("clicked at x={}, y={}.", x, y);
            }
        }
    }

    let pressed = WebEvent::KeyPress('x');
    // `to_owned()` 从一个字符串切片中创建一个具有所有权的 `String`。
    let pasted = WebEvent::Paste("my text".to_owned());
    let click = WebEvent::Click { x: 20, y: 80 };
    let load = WebEvent::PageLoad;
    let unload = WebEvent::PageUnload;

    inspect(pressed);
    inspect(pasted);
    inspect(click);
    inspect(load);
    inspect(unload);

    // 拥有隐式辨别值（implicit discriminator，从 0 开始）的 enum
    enum Number {
        Zero,
        One,
        Two,
    }

    // 拥有显式辨别值（explicit discriminator）的 enum
    enum Color {
        Red = 0xff0000,
        Green = 0x00ff00,
        Blue = 0x0000ff,
    }

    // `enum` 可以转成整型。
    println!("zero is {}", Number::Zero as i32);
    println!("one is {}", Number::One as i32);

    println!("roses are #{:06x}", Color::Red as i32);
    println!("violets are #{:06x}", Color::Blue as i32);
}

/*
 类型别名
    若使用类型别名，则可以通过其别名引用每个枚举变量。当枚举的名称太长或者太一般化，且你想要对其重命名，那么这对你会有所帮助。
 */
fn type_alias() {
    enum VeryVerboseEnumOfThingsToDoWithNumbers {
        Add,
        Subtract,
    }

    // 创建一个类型别名
    type Operations = VeryVerboseEnumOfThingsToDoWithNumbers;

    // 我们可以通过别名引用每个枚举变量，避免使用又长又不方便的枚举名字
    let x = Operations::Add;

    impl VeryVerboseEnumOfThingsToDoWithNumbers {
        fn run(&self, x: i32, y: i32) -> i32 {
            match self {
                Self::Add => x + y,
                Self::Subtract => x - y,
            }
        }
    }
}

/*
 测试实例：链表
    enum 的一个常见用法就是创建链表（linked-list）
 */
fn test_enumeration_list() {
    enum List {
        // Cons：元组结构体，包含链表的一个元素和一个指向下一节点的指针
        Cons(u32, Box<List>),
        // Nil：末结点，表明链表结束
        Nil,
    }

    // 可以为 enum 定义方法
    impl List {
        // 创建一个空的 List 实例
        fn new() -> List {
            // `Nil` 为 `List` 类型（译注：因 `Nil` 的完整名称是 `List::Nil`）
            List::Nil
        }

        // 处理一个 List，在其头部插入新元素，并返回该 List
        fn prepend(self, elem: u32) -> List {
            // `Cons` 同样为 List 类型
            List::Cons(elem, Box::new(self))
        }

        // 返回 List 的长度
        fn len(&self) -> u32 {
            // 必须对 `self` 进行匹配（match），因为这个方法的行为取决于 `self` 的
            // 取值种类。
            // `self` 为 `&List` 类型，`*self` 为 `List` 类型，匹配一个具体的 `T`
            // 类型要好过匹配引用 `&T`。
            match self {
                // 不能得到 tail 的所有权，因为 `self` 是借用的；
                // 因此使用一个对 tail 的引用
                List::Cons(_, ref tail) => 1 + tail.len(),
                // （递归的）基准情形（base case）：一个长度为 0 的空列表
                List::Nil => 0
            }
        }

        // 返回列表的字符串表示（该字符串是堆分配的）
        fn stringify(&self) -> String {
            match self {
                List::Cons(head, ref tail) => {
                    // `format!` 和 `print!` 类似，但返回的是一个堆分配的字符串，
                    // 而不是打印结果到控制台上
                    format!("{}, {}", head, tail.stringify())
                }
                List::Nil => {
                    format!("Nil")
                }
            }
        }
    }

    // 创建一个空链表
    let mut list = List::new();

    // 追加一些元素
    list = list.prepend(1);
    list = list.prepend(2);
    list = list.prepend(3);

    // 显示链表的最后状态
    println!("linked list has length: {}", list.len());
    println!("{}", list.stringify());
}

/*
 常量
    Rust 有两种常量，可以在任意作用域声明，包括全局作用域。它们都需要显式的类型声明：

    const：不可改变的值（通常使用这种）。
    static：具有 'static 生命周期的，可以是可变的变量（译注：须使用 static mut 关键字）。
    有个特例就是 "string" 字面量。它可以不经改动就被赋给一个 static 变量，因为它的类型标记：
    &'static str 就包含了所要求的生命周期 'static。其他的引用类型都必须特地声明，使之拥有 'static 生命周期。
    这两种引用类型的差异似乎也无关紧要，因为无论如何，static 变量都得显式地声明。
 */

// 全局变量是在所有其他作用域之外声明的。
static LANGUAGE: &'static str = "Rust";
const THRESHOLD: i32 = 10;

fn const_and_static() {
    fn is_big(n: i32) -> bool {
        // 在一般函数中访问常量
        n > THRESHOLD
    }

    let n = 16;

    // 在 main 函数（主函数）中访问常量
    println!("This is {}", LANGUAGE);
    println!("The threshold is {}", THRESHOLD);
    println!("{} {} {}", n, if is_big(n) { "greater than" } else { "less than" }, THRESHOLD);

    // 报错！不能修改一个 `const` 常量。
    // THRESHOLD = 5;
    // 改正 ^ 注释掉此行
}